/*
 * Copyright (c) 2020.Huawei Technologies Co., Ltd. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef DVPP_COMMON_H
#define DVPP_COMMON_H

#ifdef _WIN32
#include <windows.h>
#endif
#include "CommonDataType/CommonDataType.h"
#include "ErrorCode/ErrorCode.h"

#include "acl/ops/acl_dvpp.h"
#ifdef USE_HI_MPI
#include "acl/dvpp/hi_dvpp.h"
#endif

const int MODULUS_NUM_2 = 2;
const uint32_t ODD_NUM_1 = 1;

struct Rect {
    /* left location of the rectangle */
    uint32_t x = {};
    /* top location of the rectangle */
    uint32_t y = {};
    /* with of the rectangle */
    uint32_t width = {};
    /* height of the rectangle */
    uint32_t height = {};
};

struct DvppBaseData {
    uint32_t dataSize = {}; // Size of data in byte
    uint8_t *data = nullptr;
};

struct VdecConfig {
    int inputWidth = 0;
    int inputHeight = 0;
    acldvppStreamFormat inFormat = H264_MAIN_LEVEL;                   // stream format renference acldvppStreamFormat
    #ifndef USE_HI_MPI
    acldvppPixelFormat outFormat = PIXEL_FORMAT_YUV_SEMIPLANAR_420;   // output format renference acldvppPixelFormat
    #else
    hi_pixel_format outFormat = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420;   // output format renference acldvppPixelFormat
    #endif
    uint32_t channelId = 0;                                           // user define channelId: 0-15
    uint32_t deviceId  = 0;
    std::string outputFolder;
#ifdef _WIN32
    DWORD threadId;
    HANDLE threadHandle_ = nullptr;
#else
    pthread_t threadId = 0;                                          // thread for callback
#endif
    aclvdecCallback callback = {0};                                   // user define how to process vdec out data
    bool runflag = true;
};

struct DeviceStreamData {
    std::vector<ObjectDetectInfo> detectResult = {};
    uint32_t framId = {};
    uint32_t channelId = {};
};

struct ThreadData {
    aclrtContext context;
    VdecConfig vdecConfig;
};

const uint32_t JPEGD_STRIDE_WIDTH = 128; // Jpegd module output width need to align up to 128
const uint32_t JPEGD_STRIDE_HEIGHT = 16; // Jpegd module output height need to align up to 16
const uint32_t JPEGE_STRIDE_WIDTH = 16; // Jpege module input width need to align up to 16
const uint32_t JPEGE_STRIDE_HEIGHT = 1; // Jpege module input height remains unchanged
const uint32_t VPC_STRIDE_WIDTH = 16; // Vpc module output width need to align up to 16
const uint32_t VPC_STRIDE_HEIGHT = 2; // Vpc module output height need to align up to 2
const uint32_t VDEC_STRIDE_WIDTH = 16; // Vdec module output width need to align up to 16
const uint32_t VDEC_STRIDE_HEIGHT = 2; // Vdec module output width need to align up to 2
// If the output width is less than this value, alignment requires special handling
const uint32_t VDEC_SPECIAL_WIDTH = 16;
const uint32_t VDEC_SPECIAL_STRIDE = 32; // If the output picture width is less than 16, it must be aligned to 32
const uint32_t YUV_BYTES_NU = 3; // Numerator of yuv image, H x W x 3 / 2
const uint32_t YUV_BYTES_DE = 2; // Denominator of yuv image, H x W x 3 / 2
const uint32_t YUV422_WIDTH_NU = 2; // Width of YUV422, WidthStride = Width * 2
const uint32_t YUV444_RGB_WIDTH_NU = 3; // Width of YUV444 and RGB888, WidthStride = Width * 3
const uint32_t XRGB_WIDTH_NU = 4; // Width of XRGB8888, WidthStride = Width * 4
const uint32_t JPEG_OFFSET = 8; // Offset of input file for jpegd module
const uint32_t MAX_JPEGD_WIDTH = 8192; // Max width of jpegd module
const uint32_t MAX_JPEGD_HEIGHT = 8192; // Max height of jpegd module
const uint32_t MIN_JPEGD_WIDTH = 32; // Min width of jpegd module
const uint32_t MIN_JPEGD_HEIGHT = 32; // Min height of jpegd module
const uint32_t MAX_JPEGE_WIDTH = 8192; // Max width of jpege module
const uint32_t MAX_JPEGE_HEIGHT = 8192; // Max height of jpege module
const uint32_t MIN_JPEGE_WIDTH = 32; // Min width of jpege module
const uint32_t MIN_JPEGE_HEIGHT = 32; // Min height of jpege module
const uint32_t MAX_RESIZE_WIDTH_INPUT = 8192; // Max input width stride of resize module
const uint32_t MAX_RESIZE_HEIGHT_INPUT = 8192; // Max input height stride of resize module
const uint32_t MIN_RESIZE_WIDTH_INPUT = 32; // Min input width stride of resize module
const uint32_t MIN_RESIZE_HEIGHT_INPUT = 6; // Min input height stride of resize module
const uint32_t MAX_RESIZE_WIDTH_OUTPUT = 4096; // Max output width stride of resize module
const uint32_t MAX_RESIZE_HEIGHT_OUTPUT = 4096; // Max output height stride of resize module
const uint32_t MIN_RESIZE_WIDTH_OUTPUT = 10; // Min output width stride of resize module
const uint32_t MIN_RESIZE_HEIGHT_OUTPUT = 6; // Min output height stride of resize module
const float MIN_RESIZE_SCALE = 0.03125; // Min resize scale of resize module
const float MAX_RESIZE_SCALE = 16.0; // Min resize scale of resize module
const uint32_t MAX_VPC_WIDTH = 4096; // Max width of picture to VPC(resize/crop)
const uint32_t MAX_VPC_HEIGHT = 4096; // Max height of picture to VPC(resize/crop)
const uint32_t MIN_VPC_WIDTH  = 32;   // Min width of picture to VPC(resize/crop)
const uint32_t MIN_VPC_HEIGHT = 6;    // Min height of picture to VPC(resize/crop)
const uint32_t MIN_CROP_WIDTH = 10;   // Min width of crop area
const uint32_t MIN_CROP_HEIGHT = 6;    // Min height of crop area
const uint8_t YUV_GREYER_VALUE = 128;  // Filling value of the resized YUV image
const uint32_t REF_FRAME_NUM = 12;  // Number of reference frame
const uint32_t TIME_OUT = 1000; // Time out for send frame
const uint32_t VENC_TIME_REF = 2; // The reference time of venc
const uint32_t EPOLL_MAX_EVENTS = 3; // The max events of epoll
const uint32_t EPOLL_MAX_NUM = 10; // The max num of epoll
const uint32_t HI_VPC_STRIDE_WIDTH = 2; // HI Vpc module output width need to align up to 2
const uint32_t HI_VPC_STRIDE_HEIGHT = 2; // HI Vpc module output height need to align up to 2
const uint32_t MAX_CHANNEL_NUM = 256; // max number of channel
const uint32_t MIN_VPC_OUTPUT_WIDTH_STRIDE = 32; // Min width stride of output for VPC(resize/crop)
const uint32_t VPC_OUTPUT_HEIGHT_ALIGN = 2; // Vpc module output height need to align up to 2
const uint32_t VPC_OUTPUT_WIDTH_ALIGN = 2; // Vpc module output width need to align up to 2
const uint32_t NOT_ALIGN_DIVISOR = 2;
const uint32_t HI_VENC_CHAN_BUFF_SIZE_ALIGN = 64; // venc channel buff size need to align up to 64
const uint32_t HI_VENC_CHAN_BUFF_SIZE_FACTOR_HIGH = 5; // factor used to calculate venc channel buff size
const uint32_t HI_VENC_CHAN_BUFF_SIZE_FACTOR_LOW = 4; // factor used to calculate venc channel buff size
const uint32_t HI_VENC_MIN_CHAN_BUFF_SIZE = 384000; // min buff size of venc channel
const uint32_t JPEGE_CHAN_MIDDLE_WIDTH = 4096; // middle width for jpege channel
const uint32_t MAX_VDEC_INPUT_WIDTH = 4096; // max vdec input width
const uint32_t MIN_VDEC_INPUT_WIDTH = 128; // min vdec input width
const uint32_t MAX_VDEC_INPUT_HEIGHT = 4096; // max vdec input height
const uint32_t MIN_VDEC_INPUT_HEIGHT = 128; // min vdec input height
#define CONVERT_TO_ODD(NUM) (((NUM) % MODULUS_NUM_2 != 0) ? (NUM) : ((NUM) - 1)) // Convert the input to odd num
#define CONVERT_TO_EVEN(NUM) (((NUM) % MODULUS_NUM_2 == 0) ? (NUM) : ((NUM) - 1)) // Convert the input to even num
#define CHECK_ODD(num) ((num) % MODULUS_NUM_2 != 0)
#define CHECK_EVEN(num) ((num) % MODULUS_NUM_2 == 0)
#define RELEASE_DVPP_DATA(dvppDataPtr) do { \
    APP_ERROR retMacro; \
    if (dvppDataPtr != nullptr) { \
        retMacro = acldvppFree(dvppDataPtr); \
        if (retMacro != APP_ERR_OK) { \
            LogError << "Failed to free memory on dvpp, ret = " << retMacro << "."; \
        } \
        dvppDataPtr = nullptr; \
    } \
} while (0);

class DvppCommon {
public:
    explicit DvppCommon(aclrtStream dvppStream);
    explicit DvppCommon(const VdecConfig &vdecConfig); // Need by vdec
    ~DvppCommon();
    VdecConfig vdecConfig_;
#ifndef USE_HI_MPI
    APP_ERROR Init(void);
    APP_ERROR InitVdec();     // Needed by vdec
    APP_ERROR DeInit(void);

    static APP_ERROR GetVpcDataSize(uint32_t widthVpc, uint32_t heightVpc, acldvppPixelFormat format,
                                    uint32_t &vpcSize);
    static APP_ERROR GetVpcInputStrideSize(uint32_t width, uint32_t height, acldvppPixelFormat format,
                                           uint32_t &widthStride, uint32_t &heightStride);
    static APP_ERROR GetVpcOutputStrideSize(uint32_t width, uint32_t height, acldvppPixelFormat format,
                                            uint32_t &widthStride, uint32_t &heightStride);
    static APP_ERROR GetJpegDecodeDataSize(const void *data, uint32_t dataSize, acldvppPixelFormat format,
                                           uint32_t &decSize);
    static APP_ERROR SetEncodeLevel(uint32_t level, acldvppJpegeConfig& jpegeConfig);
    static APP_ERROR GetVideoDecodeStrideSize(uint32_t width, uint32_t height, acldvppPixelFormat format,
                                              uint32_t &widthStride, uint32_t &heightStride);
    static APP_ERROR GetVideoDecodeDataSize(uint32_t width, uint32_t height, acldvppPixelFormat format,
        uint32_t &vdecSize);

    // The following interfaces can be called only when the DvppCommon object is initialized with Init
    APP_ERROR VpcResize(DvppDataInfo &input, DvppDataInfo &output, bool withSynchronize,
                        VpcProcessType processType = VPC_PT_DEFAULT);
    APP_ERROR VpcCrop(const DvppCropInputInfo &input, const DvppDataInfo &output, bool withSynchronize);
    APP_ERROR JpegDecode(DvppDataInfo &input, DvppDataInfo &output, bool withSynchronize);

    APP_ERROR JpegEncode(DvppDataInfo &input, DvppDataInfo &output, acldvppJpegeConfig *jpegeConfig,
        bool withSynchronize) const;

    APP_ERROR GetJpegEncodeDataSize(DvppDataInfo &input, acldvppJpegeConfig *jpegeConfig, uint32_t &encSize);

    // These functions started with "Combine" encapsulate the DVPP process together, malloc DVPP memory,
    // transfer pictures from host to device, and then execute the DVPP operation.
    // The caller needs to pay attention to the release of the memory alloced in these functions.
    // You can call the ReleaseDvppBuffer function to release memory after use completely.
    APP_ERROR CombineResizeProcess(DvppDataInfo &input, DvppDataInfo &output, bool withSynchronize,
                                   VpcProcessType processType = VPC_PT_DEFAULT);
    APP_ERROR CombineCropProcess(DvppCropInputInfo &input, DvppDataInfo &output, bool withSynchronize);
    APP_ERROR CombineJpegdProcess(const RawData& imageInfo, acldvppPixelFormat format, bool withSynchronize);
    APP_ERROR CombineJpegeProcess(const RawData& imageInfo, uint32_t width, uint32_t height, acldvppPixelFormat format,
                                  bool withSynchronize);
    // The following interface can be called only when the DvppCommon object is initialized with InitVdec
    APP_ERROR CombineVdecProcess(std::shared_ptr<DvppDataInfo> data, void *userData);
    APP_ERROR VdecSendEosFrame() const;
    APP_ERROR GetNotAlignBuffer(const CropRoiConfig &cropArea);
    APP_ERROR SetPicDescData(const DvppDataInfo &output);
#else
    APP_ERROR CerateJpegeChannel(int width, int height);
    APP_ERROR HiInitJpegd(int width, int height);
    APP_ERROR HiDeInitJpegd(void);
    APP_ERROR HiInitJpege(int width, int height);
    APP_ERROR HiDeInitJpege(void);
    APP_ERROR CreateVdecChannel();
    APP_ERROR SetVdecChnParm();
    APP_ERROR HiInitVdec();
    APP_ERROR HiDeInitVdec();
    APP_ERROR HiInitVpc(void);
    APP_ERROR HiDeInitVpc(void);

    // decode image
    APP_ERROR HiJpegDecode(DvppDataInfo &input, DvppDataInfo &output, bool withSynchronize);
    static APP_ERROR HiGetJpegDecodeDataSize(uint32_t width, uint32_t height, hi_pixel_format format,
                                             uint32_t &decSize);
    APP_ERROR HiCombineJpegdProcess(const RawData& imageInfo, hi_pixel_format format, bool withSynchronize);
    APP_ERROR HiGetJpegdOutputInfo(DvppDataInfo &input, hi_vdec_pic_info &outPicInfo, hi_vdec_stream &stStream);

    // encode image
    APP_ERROR HiCombineJpegeProcess(const RawData& imageInfo, uint32_t width, uint32_t height, hi_pixel_format format,
                                    bool withSynchronize);
    APP_ERROR HiVencSendFrame(hi_pixel_format format);
    APP_ERROR HiVencGetStream(hi_venc_stream &stream);
    APP_ERROR HiVencGetResult(hi_venc_stream &stream);
    APP_ERROR CreateEpoll();
    APP_ERROR CloseEpoll();

    // decode video
    static APP_ERROR HiGetVideoDecodeStrideSize(uint32_t width, uint32_t height, hi_pixel_format format,
                                                uint32_t &widthStride, uint32_t &heightStride);
    static APP_ERROR HiGetVideoDecodeDataSize(uint32_t width, uint32_t height, hi_pixel_format format,
                                              uint32_t &vdecSize);
    APP_ERROR HiCombineVdecProcess(std::shared_ptr<DvppDataInfo> data, void *userData);
    APP_ERROR HiVdecSendStream(void *InDevBuff, uint32_t inputDataSize, DvppDataInfo dataInfo);
    APP_ERROR HiGetVdecOutputInfo(void *picOutBufferDev, DvppDataInfo &dataInfo);
    APP_ERROR VdecChnQueryStatus(bool &chnStatus);
    APP_ERROR HiVdecSendEndStream();
    APP_ERROR HiVdecGetFrame(hi_video_frame_info &frame, bool &decResult, uint32_t count);
    APP_ERROR HiVdecRelease(hi_video_frame_info &frame, hi_vdec_stream &stream);
    APP_ERROR PrepareToWrite(hi_video_frame_info frame, uint32_t count);

    // vpc
    static APP_ERROR HiGetVpcDataSize(uint32_t widthVpc, uint32_t heightVpc, hi_pixel_format format,
                                      uint32_t &vpcSize);
    static APP_ERROR HiGetVpcOutputStrideSize(uint32_t width, uint32_t height, hi_pixel_format format,
                                              uint32_t &widthStride, uint32_t &heightStride);
    static APP_ERROR HiGetVpcInputStrideSize(uint32_t width, uint32_t height, hi_pixel_format format,
                                             uint32_t &widthStride, uint32_t &heightStride);
    APP_ERROR HiVpcResize(DvppDataInfo &input, DvppDataInfo &output, bool withSynchronize,
                          VpcProcessType processType = VPC_PT_DEFAULT);
    APP_ERROR HiVpcCrop(const DvppCropInputInfo &input, const DvppDataInfo &output, bool withSynchronize);
    APP_ERROR HiCombineResizeProcess(DvppDataInfo &input, DvppDataInfo &output, bool withSynchronize,
                                     VpcProcessType processType = VPC_PT_DEFAULT);
    APP_ERROR HiCombineCropProcess(DvppCropInputInfo &input, DvppDataInfo &output, bool withSynchronize);
    APP_ERROR HiGetNotAlignBuffer(hi_vpc_pic_info &outputPic);
    bool WriteToFile(const std::string &fileName, uint8_t* dataDev, uint32_t dataSize);
    void SetDeviceId(int32_t deviceId);
    void SetChnId(int32_t chnId);
    void SetContext(aclrtContext &context);
    aclrtContext GetContext();
    void SetRunFlag(bool runFlag);
    bool GetRunFlag();
#endif
    static APP_ERROR GetJpegImageInfo(const void *data, uint32_t dataSize, uint32_t &width, uint32_t &height,
                                      int32_t &components);
    static APP_ERROR GetJpegEncodeStrideSize(std::shared_ptr<DvppDataInfo> &input);
    static void GetJpegDecodeStrideSize(uint32_t width, uint32_t height, uint32_t &widthStride, uint32_t &heightStride);
    // Get the private member variables which are assigned in the interfaces which are started with "Combine"
    std::shared_ptr<DvppDataInfo> GetInputImage() const;
    std::shared_ptr<DvppDataInfo> GetDecodedImage() const;
    std::shared_ptr<DvppDataInfo> GetResizedImage() const;
    std::shared_ptr<DvppDataInfo> GetEncodedImage() const;
    std::shared_ptr<DvppDataInfo> GetCropedImage() const;

    // Release the memory that is allocated in the interfaces which are started with "Combine"
    void ReleaseDvppBuffer() const;
    APP_ERROR CheckVdecConfig();
private:
#ifndef USE_HI_MPI
    APP_ERROR SetDvppPicDescData(const DvppDataInfo &dataInfo, acldvppPicDesc &picDesc) const;
    APP_ERROR ResizeProcess(acldvppPicDesc &inputDesc, acldvppPicDesc &outputDesc, bool withSynchronize);
    APP_ERROR ResizeWithPadding(acldvppPicDesc &inputDesc, acldvppPicDesc &outputDesc, CropRoiConfig &cropRoi,
                                CropRoiConfig &pasteRoi, bool withSynchronize);
    APP_ERROR CropProcess(acldvppPicDesc &inputDesc, acldvppPicDesc &outputDesc, const CropRoiConfig &cropArea,
                          bool withSynchronize);
    APP_ERROR CreateStreamDesc(std::shared_ptr<DvppDataInfo> data);
    APP_ERROR DestroyResource();
#else
    int32_t deviceId_ = 0;
    int32_t chnId_ = 0;
    bool epollExist_ = true;
    int32_t epollFd_ = 0;
    int32_t fd_ = 0;
    aclrtContext context_ = nullptr;
    bool runFlag_;
    APP_ERROR HiCropProcess(hi_vpc_pic_info inputDesc, hi_vpc_pic_info &outputDesc, const CropRoiConfig &cropArea,
                            bool withSynchronize);
#endif
    APP_ERROR CheckCropParams(const DvppCropInputInfo &input) const;
    APP_ERROR CheckResizeParams(const DvppDataInfo &input, const DvppDataInfo &output) const;
    APP_ERROR TransferImageH2D(const RawData& imageInfo, const std::shared_ptr<DvppDataInfo>& jpegInput) const;
    void GetCropRoi(const DvppDataInfo &input, const DvppDataInfo &output, VpcProcessType processType,
                         CropRoiConfig &cropRoi) const;
    void GetPasteRoi(const DvppDataInfo &input, const DvppDataInfo &output, VpcProcessType processType,
                         CropRoiConfig &pasteRoi) const;
    std::shared_ptr<acldvppRoiConfig> cropAreaConfig_ = nullptr;
    std::shared_ptr<acldvppRoiConfig> pasteAreaConfig_ = nullptr;

    std::shared_ptr<acldvppPicDesc> cropInputDesc_ = nullptr;
    std::shared_ptr<acldvppPicDesc> cropOutputDesc_ = nullptr;
    std::shared_ptr<acldvppRoiConfig> cropRoiConfig_ = nullptr;

    std::shared_ptr<acldvppPicDesc> encodeInputDesc_ = nullptr;
    std::shared_ptr<acldvppJpegeConfig> jpegeConfig_ = nullptr;

    std::shared_ptr<acldvppPicDesc> resizeInputDesc_ = nullptr;
    std::shared_ptr<acldvppPicDesc> resizeOutputDesc_ = nullptr;
    std::shared_ptr<acldvppResizeConfig> resizeConfig_ = nullptr;

    std::shared_ptr<acldvppPicDesc> decodeOutputDesc_ = nullptr;

    acldvppChannelDesc *dvppChannelDesc_ = nullptr;
    aclrtStream dvppStream_ = nullptr;
    std::shared_ptr<DvppDataInfo> inputImage_ = nullptr;
    std::shared_ptr<DvppDataInfo> decodedImage_ = nullptr;
    std::shared_ptr<DvppDataInfo> encodedImage_ = nullptr;
    std::shared_ptr<DvppDataInfo> resizedImage_ = nullptr;
    std::shared_ptr<DvppDataInfo> cropImage_ = nullptr;
    bool isVdec_ = false;
    aclvdecChannelDesc *vdecChannelDesc_ = nullptr;
    acldvppStreamDesc *streamInputDesc_ = nullptr;
    acldvppPicDesc *picOutputDesc_ = nullptr;
};
#endif
